package calculation;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import my_interface.SelectType;

/**
 *
 * @author KieuOanh
 */
public class FIROptimalEquiripple implements SelectType {

    public int typeSelect;
    public double wp;
    public double ws;
    public double As;
    public double Rp;
    //
    public double w2p;
    public double w2s;
    //
    public double delta;
    public double delta1;
    public double delta2;
    public double deltaH;
    public double deltaL;
    public double deltaf;
    public double delta_w;
    public final double pi = Math.PI;
    public int M;
    public int Mnew;
    //
    //remez
    int hSize;
    int numtaps;
    int numband;
    int type = 1;
    double[] bands;
    double[] des;
    double[] weight;
    public Remez remez;
    //
    public double[] f;
    public double[] weights;
    public double[] m;
    public double[] hn;
    public double[] dB;
    public double[] Hw;
    //
    public double hnMax;
    public double hnMin;
    public double HwMax;
    public double HwMin;
    public double dBMax;
    public double dBMin;

    public FIROptimalEquiripple(int typeSelect, double WP, double WS, double AS, double RP) {
        this.typeSelect = typeSelect;
        this.wp = WP;
        this.ws = WS;
        this.As = AS;
        this.Rp = RP;
//        this.M = M;

        contructArrayH(M);
        setValueDelta();
        System.out.println("delta= " + delta);
        setValueM();
        System.out.println("delta1= " + delta1);
        System.out.println("delta2= " + delta2);
        setValueWFM();

        System.out.println("M= " + M);
        hSize = M;
        numtaps = M;
        numband = weights.length;
        System.out.println("numband: " + numband);
        bands = f;
        weight = weights;
        des = m;
        //
        remez = new Remez(hSize, numtaps, numband, bands, des, weight, type);
        hn = remez.h;

        setValueForDB(M);
        findMaxMin();
        FileWriter fstream = null;
        try {
            fstream = new FileWriter("D://FIR Optimal Equiripple.txt");
        } catch (IOException ex) {
            Logger.getLogger(FIRFrequenceSample.class.getName()).log(Level.SEVERE, null, ex);
        }
      
        BufferedWriter out = new BufferedWriter(fstream);
        
        for (int i = 0; i < M; i++) {
            System.out.println("hn[" + i + "]= " + hn[i]);
            try {
                out.write("\nhn[" + i + "]= " + hn[i]);
            } catch (IOException ex) {
                Logger.getLogger(FIRFrequenceSample.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        try {
            out.close();
        } catch (IOException ex) {
            Logger.getLogger(FIRFrequenceSample.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public FIROptimalEquiripple(int typeSelect, double WP, double WS, double AS, double RP, double W2P, double W2S) {
        this.typeSelect = typeSelect;
        this.wp = WP;
        this.ws = WS;
        this.w2s = W2S;
        this.w2p = W2P;
        this.As = AS;
        this.Rp = RP;

        contructArrayH(M);
        setValueDelta();
        setValueM();
        setValueWFM();

        System.out.println("M= " + M);
        hSize = M;
        numtaps = M;
        numband = weights.length;
        bands = f;
        weight = weights;
        des = m;
        remez = new Remez(hSize, numtaps, numband, bands, des, weight, type);
        hn = remez.h;
        setValueForDB(M);
        findMaxMin();
        FileWriter fstream = null;
        try {
            fstream = new FileWriter("D://FIR Optimal Equiripple.txt");
        } catch (IOException ex) {
            Logger.getLogger(FIRFrequenceSample.class.getName()).log(Level.SEVERE, null, ex);
        }
        BufferedWriter out = new BufferedWriter(fstream);
        for (int i = 0; i < M; i++) {
            System.out.println("hn[" + i + "]= " + hn[i]);
            try {
                out.write("\nhn[" + i + "]= " + hn[i]);
            } catch (IOException ex) {
                Logger.getLogger(FIRFrequenceSample.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        try {
            out.close();
        } catch (IOException ex) {
            Logger.getLogger(FIRFrequenceSample.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public int setValueM() {
        switch (typeSelect) {
            case LOW_PASS:
                deltaf = (ws - wp) / 2;
                break;
            case HIGH_PASS:
                deltaf = (wp - ws) / 2;
                break;
            case BAND_PASS:
                deltaf = min((wp - ws) / 2, (w2s - w2p) / 2);
                break;
            case BAND_STOP:
                deltaf = min((ws - wp) / 2, (w2p - w2s) / 2);
                break;
        }
        System.out.println("delta: " + deltaf);
        M = round((-10 * log10(delta1 * delta2) - 13) / (14.6 * deltaf)) + 1;
        return M;
    }

    public void contructArrayH(int M) {
        hn = new double[M];
        Hw = new double[300];
        dB = new double[300];

    }

    public void setValueWFM() {
        switch (typeSelect) {
            case LOW_PASS:
                f = new double[]{0, wp / 2, ws / 2, 0.5};
                weights = new double[]{delta, 1};
                m = new double[]{1, 0};
                break;
            case HIGH_PASS:
                f = new double[]{0, ws / 2, wp / 2, 0.5};
                weights = new double[]{1, delta};
                m = new double[]{0, 1};
                break;
            case BAND_PASS:
                f = new double[]{0, ws / 2, wp / 2, w2p / 2, w2s / 2, 0.5};
                weights = new double[]{1, delta, 1};
                m = new double[]{0, 1, 0};
                break;
            case BAND_STOP:
                f = new double[]{0, wp / 2, ws / 2, w2s / 2, w2p / 2, 0.5};
                weights = new double[]{delta, 1, delta};
                m = new double[]{1, 0, 1};
                break;
        }
    }

    public double setValueDelta() {
        delta1 = (pow10(Rp / 20) - 1) / (pow10(Rp / 20) + 1);
        delta2 = (1 + delta1) * pow10(-As / 20);
        delta = delta2 / delta1;
        return delta;
    }

    public void findMaxMin() {
        for (int i = 0; i < M; i++) {
            hnMax = max(hnMax, hn[i]);
            hnMin = min(hnMin, hn[i]);
        }
        for (int w = 0; w < 300; w++) {
            HwMax = max(HwMax, Hw[w]);
            HwMin = min(HwMin, Hw[w]);
        }
        System.out.println("hnmax: " + hnMax);
        System.out.println("hnmin: " + hnMin);

    }

    public void setValueForDB(int M) {

        int count = 0;
        while (As > dBMin) {
            System.out.println("count: " + count++);
            M++;
            System.out.println("M: " + M);
            contructArrayH(M);
            setValueDelta();
            setValueWFM();
            hSize = M;
            numtaps = M;
            numband = weights.length;
            bands = f;
            weight = weights;
            des = m;
            remez = new Remez(hSize, numtaps, numband, bands, des, weight, type);
            hn = remez.h;

            double real = 0;
            double imag = 0;
            for (int w = 0; w < 300; w++) {
                real = 0;
                imag = 0;
                for (int n = 0; n < M; n++) {
                    real += hn[n] * cos(w * n * pi / 300);
                    imag += hn[n] * sin(w * n * pi / 300);
                }

                Hw[w] = module(real, imag);
                HwMax = max(HwMax, Hw[w]);

            }
            for (int w = 0; w < 300; w++) {
                dB[w] = -20 * log10(Hw[w] / HwMax);
                dBMax = max(dBMax, dB[w]);
            }
            dBMin = dBMax;
            int tempW = 0;
            int tempW2 = 0;
            switch (typeSelect) {
                case LOW_PASS:
                    tempW = (int) (ws * 300);
                    for (int i = tempW; i < 300; i++) {
                        dBMin = min(dBMin, dB[i]);
                    }
                    break;
                case HIGH_PASS:
                case BAND_PASS:
                    tempW = (int) (ws * 300) + 1;
                    for (int i = 0; i < tempW; i++) {
                        dBMin = min(dBMin, dB[i]);
                    }
                    break;
                case BAND_STOP:
                    tempW = (int) (ws * 300);
                    tempW2 = (int) (w2s * 300);
                    for (int i = tempW; i < tempW2; i++) {
                        dBMin = min(dBMin, dB[i]);
                    }
                    break;
            }
            System.out.println("tempW: " + tempW);
            System.out.println("dBMax= " + dBMax);
            System.out.println("dBMin= " + dBMin);
        }
        this.M = M;
    }

    double max(double x, double y) {
        return Math.max(x, y);
    }

    double min(double x, double y) {
        return Math.min(x, y);
    }

    double sin(double x) {
        return Math.sin(x);
    }

    double cos(double x) {
        return Math.cos(x);
    }

    int round(double x) {
        return (int) Math.round(x);
    }

    double log10(double x) {
        return Math.log10(x);
    }

    double sqrt(double x) {
        return Math.sqrt(x);
    }

    double module(double real, double imag) {
        return sqrt(real * real + imag * imag);
    }

    double pow10(double x) {
        return Math.pow(10.0, x);
    }
}
